/* ===========================================================================
 *
 *       Filename:  xyb_handwriting.cpp
 *
 *    Description:  handwriting, xyb (qt)
 *
 * - Mode: 644
 * - NOTE. NOT use "dos"
 * - Updated: 2015-04-26 17:11:11
 *
 *        Version:  1.0.0
 *        Created:  2015-04-16 10:13:08
 *       Revision:  1.0.0
 *       Compiler:  g++
 *
 *         Author:  Yui Wong, email: yuiwong@126.com
 *   Organization:  ""
 *        License:  LGPLv3
 *
 *  This file is part of portable-inputmethod.yw.
 *
 *  portable-inputmethod.yw
 *  is free software: you can redistribute it
 *  and/or modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  portable-inputmethod.yw
 *  is distributed in the hope that it will be
 *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with portable-inputmethod.yw.
 *  If not, see <http://www.gnu.org/licenses/>.
 *
 * ===========================================================================
 */


#if defined(PIMETHOD_DEBUG)
#	include <QtCore/QDebug>
#endif
#include <QtGui/QPainter> /* QPainter */

#include <xyb_handwriting.h> /* class xyb_handwriting */
#include <simp_sync_mutx.h>

#if (ID_QT_VERSION < 0x050000)
#	include <QtGui/QApplication>
#else
#	include <QtWidgets/QApplication>
#endif


size_t xyb_handwriting::writor_width_fixed = 400;
size_t xyb_handwriting::writor_height_fixed = 400;
int xyb_handwriting::writor_pen_w_fixed = 2;/* 4 */


#define __STLST_ALL \
	"background-color: #87cefa; color: #ff0000; font-weight: bold; " \
	"border-radius: 8px"


#define ZHHXYB_AE_ARG2(__0_ae_tp) ((int)(__0_ae_tp))
#define ZHHXYB_MAXMF_LEN (127)
#define ZHHXYB_MF_DFT "hci711.mrd"
#define ZHHXYB_CHARSET_IDS_COUNT (16)
#define ZHHXYB_MAX_CANDIDATE_COUNT (50)

/*
 * static data
 */
char xyb_handwriting::sn[] = "demo.xyb.ksense.com";/* series number */
/* application environment */
const xyb_handwriting::ae_t xyb_handwriting::ae
	= xyb_handwriting::ae_non_embedded;
/* model file */
char xyb_handwriting::mf[ZHHXYB_MAXMF_LEN + 1] = ZHHXYB_MF_DFT;
// const int xyb_handwriting::recg_flag = 0x00077007 | 0x20000000L;
const int xyb_handwriting::recg_flag = 0;
/* GBK */
const int xyb_handwriting::recg_charset_ids[]
	= { 5, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0 };
int xyb_handwriting::candidate_charcodes[ZHHXYB_MAX_CANDIDATE_COUNT];
int xyb_handwriting::candidate_charcodes_pc
	[ZHHXYB_MAX_CANDIDATE_COUNT];
const int xyb_handwriting::max_candidate_count = ZHHXYB_MAX_CANDIDATE_COUNT;


xyb_handwriting::xyb_handwriting (QWidget * parent)
	: inputmethod(parent) {
	int i;
	QSpacerItem * spacer;
	QPalette palette;
	QFont font;

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	/*
	 * STAGE-I: pre-init
	 */
	this->access = 1;
	this->allpage = 0;
	this->index = 0;
	this->writor_layout = NULL;
	this->option_layout = NULL;
	this->candidate_layout = NULL;
	this->focus = NULL;
	this->core_initialized = false;

	/*
	 * STAGE-I-II: deinit core
	 */
	this->do_deinit_core();

	/*
	 * STAGE-II: ui
	 */
	/* writor */
	this->setContentsMargins(0, 0, 0, 0);
	this->setFixedSize(writor_width_fixed, writor_height_fixed);
	this->setAutoFillBackground(true);
	palette.setColor(QPalette::Background, QColor(0xff, 0xff, 0xff));
	this->setPalette(palette);

	this->master_layout= new QVBoxLayout(this);


	this->writor_layout = new QHBoxLayout();
	this->writor_layout->setContentsMargins(0, 0, 0, 0);


	this->option_layout = new QVBoxLayout();
	this->option_layout->setContentsMargins(0, 0, 0, 0);

	this->up = new QPushButton(tr("<<"), this);
	this->up->setFixedSize(36, 36);
	font.setPixelSize(14);
	this->up->setFont(font);
	this->up->setStyleSheet(__STLST_ALL);

	this->down = new QPushButton(tr(">>"), this);
	this->down->setFixedSize(36, 36);
	this->down->setFont(font);
	this->down->setStyleSheet(__STLST_ALL);

	spacer = new QSpacerItem(36, 24);
	this->option_layout->addItem(spacer);
	this->option_layout->addWidget(this->up);
	spacer = new QSpacerItem(36, 24);
	this->option_layout->addItem(spacer);
	this->option_layout->addWidget(this->down);
	spacer = new QSpacerItem(36, 24);
	this->option_layout->addItem(spacer);

	this->option_layout->setAlignment(this->up,
		Qt::AlignRight | Qt::AlignVCenter);
	this->option_layout->setAlignment(this->down,
		Qt::AlignRight | Qt::AlignVCenter);

	this->option_layout->setStretch(0, 1);
	this->option_layout->setStretch(1, 1);
	this->option_layout->setStretch(2, 1);
	this->option_layout->setStretch(3, 1);
	this->option_layout->setStretch(4, 1);


	this->candidate_layout = new QHBoxLayout();
	this->candidate_layout->setContentsMargins(5, 0, 5, 0);
	/* ready candidate_btns */
	for (i = 0; i < XYB_HW_CB_NUM; ++i) {
		candidate_btns[i] = new QPushButton(this);
		candidate_btns[i]->setFixedSize(36, 36);
		candidate_btns[i]->setFont(font);
		candidate_btns[i]->setStyleSheet(__STLST_ALL);
		this->candidate_layout->addWidget(candidate_btns[i]);
	}


	spacer = new QSpacerItem(xyb_handwriting::writor_width_fixed - 64 - 10,
		xyb_handwriting::writor_height_fixed - 64 - 10);
	this->writor_layout->addItem(spacer);

	this->writor_layout->addLayout(this->option_layout, 1);
	this->writor_layout->setAlignment(this->option_layout, Qt::AlignCenter);

	this->master_layout->addLayout(this->writor_layout, 0);
	this->master_layout->addLayout(this->candidate_layout, 1);

	for (i = 0; i < XYB_HW_CB_NUM; ++i) {
		this->candidate_layout->setAlignment(this->candidate_btns[i],
			Qt::AlignHCenter);
	}

	for (i = 0; i < XYB_HW_CB_NUM; ++i) {
		QObject::connect(this->candidate_btns[i], SIGNAL(clicked()), this,
		SLOT(choose_char()));
	}
	QObject::connect(this->up, SIGNAL(clicked()), this, SLOT(turnpageup()));
	QObject::connect(this->down, SIGNAL(clicked()), this,
		SLOT(turnpagedown()));
	QObject::connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)),
		this, SLOT(restore_last_focus(QWidget*, QWidget*)));

	this->genbuttonstate();
} /* xyb_handwriting::xyb_handwriting */


void xyb_handwriting::do_deinit_core (void) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	if (! this->core_initialized) {
#		if defined(PIMETHOD_DEBUG)
		qDebug() << __FILE__ << __LINE__ << __func__ << "NOT-NEEDED";
#		endif /* if defined(PIMETHOD_DEBUG) */
		return;
	}

	if (this->recogtimer.isActive()) {
		this->recogtimer.stop();
	}
	this->recogtimer.disconnect();

	if (! this->pnts_c.isEmpty()) {
		this->pnts_c.clear();
	}

	if (! this->pnts_r.isEmpty()) {
		this->pnts_r.clear();
	}

	if (! this->pnts_e.isEmpty()) {
		this->pnts_e.clear();
	}

	memset(xyb_handwriting::mf, 0, ZHHXYB_MAXMF_LEN + 1);
	strncpy(xyb_handwriting::mf, ZHHXYB_MF_DFT, ZHHXYB_MAXMF_LEN);

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__ << "BEFORE: ZZ_DeleteLib";
#	endif /* if defined(PIMETHOD_DEBUG) */

	ZZ_DeleteLib();

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__ << "AFTER: ZZ_DeleteLib";
#	endif /* if defined(PIMETHOD_DEBUG) */

	this->core_initialized = false;
} /* xyb_handwriting::do_deinit_core */


void xyb_handwriting::CORE_READY (void) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	this->core_initialized = true;
} /* xyb_handwriting::CORE_READY */


int xyb_handwriting::reinitialize_core (QWidget * focus,
	const char * mf) {
	int retval;
	char mfdmp[ZHHXYB_MAXMF_LEN + 1];

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	/**/capture_lock((this->access));

	/*
	 * pre-ready: deinit-core
	 */
	this->do_deinit_core();

	if (! focus) {
		this->focus = focus;
	}

	/*
	 * STAGE-III: recognizer-init
	 */
	/*
	 * create recognizer engine
	 *
	 * - 注册码：demo.xyb.ksense.com
	 *
	 * - 初始化时需要传参进去:
	 *   例如: ret = ZZ_CreateLib("demo.xyb.ksense.com",0, "hci711.mrd");
	 *
	 * - 正式注册后再提供无限制注册码
	 */
	if (mf) {
		memset(mfdmp, 0, ZHHXYB_MAXMF_LEN + 1);
		strncpy(mfdmp, mf, ZHHXYB_MAXMF_LEN);
#		if defined(PIMETHOD_DEBUG)
		qDebug() << __FILE__ << __LINE__ << __func__ << mfdmp;
#		endif /* if defined(PIMETHOD_DEBUG) */
		retval = ZZ_CreateLib(xyb_handwriting::sn,
			ZHHXYB_AE_ARG2(xyb_handwriting::ae), mfdmp);
	} else {
		/* retval = ZZ_CreateLib("demo.xyb.ksense.com",0, "hci711.mrd"); */
		retval = ZZ_CreateLib(xyb_handwriting::sn,
			ZHHXYB_AE_ARG2(xyb_handwriting::ae), xyb_handwriting::mf);
	}
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
	perror("ZZ_CreateLib");
#	endif /* if defined(PIMETHOD_DEBUG) */
	if (1 != retval) {
		/* fail */
		fprintf(stderr, "ERROR: %s: ZZ_CreateLib fail\n", __func__);
		fflush(stderr);
		retval = inputmethod_manager::errno_zz_create_lib_fail * -1;
		goto back;
	} /* (1 != retval) */

	/* ok to record it when needed */
	if (mf) {
		strncpy(xyb_handwriting::mf, mfdmp, ZHHXYB_MAXMF_LEN);
	}

	/*
	 * STAGE-III-II: timer
	 */
	/* ready to recognize: 1s */
	this->recogtimer.setInterval(1e3);
	this->recogtimer.stop();
	this->recogtimer.disconnect();
	QObject::connect(&(this->recogtimer), SIGNAL(timeout()),
		this, SLOT(recognize()));

	/* ok to set core_initialized true */
	this->CORE_READY();
	retval = 0;

back:
	rel_lock((this->access));/**/

	return  retval;
} /* xyb_handwriting::reinitialize_core */


xyb_handwriting::~xyb_handwriting (void) {
	int i;

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	/**/capture_lock((this->access));

	this->do_deinit_core();

	for (i = 0; i < XYB_HW_CB_NUM; ++i) {
		QObject::disconnect(this->candidate_btns[i], SIGNAL(clicked()), this,
		SLOT(choose_char()));
		delete this->candidate_btns[i];
		this->candidate_btns[i] = NULL;
	}
	QObject::disconnect(this->up, SIGNAL(clicked()), this,
		SLOT(turnpageup()));
	delete this->up;
	this->up = NULL;
	QObject::disconnect(this->down, SIGNAL(clicked()), this,
		SLOT(turnpagedown()));
	delete this->down;
	this->down = NULL;
	QObject::disconnect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)),
		this, SLOT(restore_last_focus(QWidget*, QWidget*)));

	if (this->master_layout) {
		QVBoxLayout * d = this->master_layout;
		this->master_layout = NULL;
		delete d;
	}

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__ << "DONE";
#	endif /* if defined(PIMETHOD_DEBUG) */

	rel_lock((this->access));/**/
} /* xyb_handwriting::~xyb_handwriting */


void xyb_handwriting::restore_last_focus (QWidget * oldf,
	QWidget * newf) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	if ((newf != 0) && (! this->is_self(newf))) {
		/* when exit newf NULL -- XXX-FIXED */
		this->focus = newf;
	}
	if (newf != 0) {
		/* when exit newf NULL -- XXX-FIXED */
#		if defined(PIMETHOD_DEBUG)
		qDebug() << __FILE__ << __LINE__ << __func__ << "RESTORE";
#		endif /* if defined(PIMETHOD_DEBUG) */
		this->focus->setFocus();
	}
} /* xyb_handwriting::restore_last_focus */


bool xyb_handwriting::is_self (const QWidget * w) {
	if ((long int)w == (long int)this) {
		return true;
	}

	for (int i = 0; i < XYB_HW_CB_NUM; ++i) {
		if ((long int)w == (long int)candidate_btns[i]) {
			return true;
		}
	}

	if ((long int)w == (long int)up) {
		return true;
	}

	if ((long int)w == (long int)down) {
		return true;
	}

	return false;
} /* xyb_handwriting::is_self */


void xyb_handwriting::mousePressEvent (QMouseEvent *evt) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	if (this->mouse_state == inputmethod_manager::click_state_choose) {
		this->pnts_c.clear();
		this->pnts_r.clear();
		this->pnts_e.clear();
		this->mouse_state = inputmethod_manager::click_state_press;
		this->recogtimer.stop();
		update();
		return;
	}

	this->mouse_state = inputmethod_manager::click_state_press;
	this->recogtimer.stop();

	this->pnts_c.append((short)evt->x());
	this->pnts_r.append((short)evt->y());
	this->pnts_e.append(0x0);
} /* xyb_handwriting::mousePressEvent */


void xyb_handwriting::mouseMoveEvent (QMouseEvent * evt) {
	static int count = 0;

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	if ((inputmethod_manager::click_state_press
		| inputmethod_manager::click_state_move) == mouse_state) { /* 鼠标按下状态 */
		this->pnts_c.enqueue((short)(evt->x()));
		this->pnts_r.enqueue((short)(evt->y()));
		this->pnts_e.enqueue(0x0);
	}
	mouse_state = inputmethod_manager::click_state_move;
	++count;
	if (0 == (count % XYB_HW_CB_NUM)) {
		update();
	}
} /* xyb_handwriting::mouseMoveEvent */


void xyb_handwriting::mouseReleaseEvent (QMouseEvent * evt) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	if (inputmethod_manager::click_state_move != mouse_state) {
#	if defined(PIMETHOD_DEBUG)
		qDebug() << __FILE__ << __LINE__ << __func__ << "NOT MOVING";
#	endif /* if defined(PIMETHOD_DEBUG) */
		return;
	}

	/*
	 * ready for recognition
	 */
	this->pnts_c.enqueue(evt->x());
	this->pnts_r.enqueue(evt->y());
	this->pnts_e.enqueue(0x0001);
	update();
	this->mouse_state = inputmethod_manager::click_state_release;
	this->recogtimer.setInterval(1e3);/* 1 s */
	this->recogtimer.disconnect();
	QObject::connect(&(this->recogtimer), SIGNAL(timeout()),
		this, SLOT(recognize()));
	this->recogtimer.start();
} /* xyb_handwriting::mouseReleaseEvent */


void xyb_handwriting::genbuttonstate (void) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	this->up->hide();
	this->down->hide();
	for (int i = 0; i < XYB_HW_CB_NUM; ++i) {
		candidate_btns[i]->hide();
		if (index<allpage) {
			if (index * XYB_HW_CB_NUM + i < this->candidate_count) {
				candidate_btns[i]->setText(QString::fromUcs4(
					(uint32_t *)&(this->candidate_charcodes[
					index * XYB_HW_CB_NUM + i]), 1)
					);
				candidate_btns[i]->show();
				this->up->show();
				this->down->show();
			}
		}
	}
} /* xyb_handwriting::genbuttonstate */


void xyb_handwriting::turnpageup (void) {
	if (index>0) {
		index--;
	}
	genbuttonstate();
} /* xyb_handwriting::turnpageup */


void xyb_handwriting::turnpagedown (void) {
	if (index<allpage-1) {
		index++;
	}
	genbuttonstate();
} /* xyb_handwriting::turnpagedown */


void xyb_handwriting::choose_char (void) {
	static char c[6 + 1];
	QString chose;

	/*
	QPushButton* push=static_cast<QPushButton *>(sender());
	emit char_chose(push->text()[0]);
	*/

	QPushButton * b = static_cast<QPushButton *>(sender());
	chose.clear();
	chose = b->text();
	memset(c, 0, 6 + 1);
	qstrncpy(c, chose.toUtf8().data(), 6);

	/*
	 * POST OUT
	 */
	emit char_chose(this->focus, c);

	fprintf(stdout, "SELECTED: %s\n", c);
	fflush(stdout);

	this->up->hide();
	this->down->hide();
	for (int i = 0; i < XYB_HW_CB_NUM;++i) {
		candidate_btns[i]->hide();
	}
} /* xyb_handwriting::choose_char */


void xyb_handwriting::recognize (void) {
	int i, n;
	short * dmp_c, * dmp_r, * dmp_e;

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	this->recogtimer.stop();
	mouse_state = inputmethod_manager::click_state_choose;

	/*
	 * real recognize
	 */
	n = this->pnts_c.count();
	dmp_c = new short[n];
	dmp_r = new short[n];
	dmp_e = new short[n];

	for (i = 0; i < n; ++ i) {
		dmp_c[i] = pnts_c.dequeue();
		dmp_r[i] = pnts_r.dequeue();
		dmp_e[i] = pnts_e.dequeue();
	}

	/*
	 * recg
	 */
	this->candidate_count = ZZ_RecgTuxg(
		xyb_handwriting::recg_flag,
		(int *)xyb_handwriting::recg_charset_ids,
		NULL,
		dmp_c,
		dmp_r,
		dmp_e,
		n,
		this->candidate_charcodes,
		this->candidate_charcodes_pc,
		this->max_candidate_count,
		NULL,
		NULL);

	/*
	 * release res
	 */
	delete dmp_c;
	delete dmp_r;
	delete dmp_e;
	dmp_c = NULL;
	dmp_r = NULL;
	dmp_e = NULL;

	if (! this->pnts_c.isEmpty()) {
		this->pnts_c.clear();
		this->pnts_r.clear();
		this->pnts_e.clear();
	}

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__ << this->candidate_count
		<< n;
	perror("ZZ_RecgTuxg");
#	endif /* if defined(PIMETHOD_DEBUG) */

	this->index = 0;

	/* 向上取整 实现方法 */
	this->allpage = (((this->candidate_count % XYB_HW_CB_NUM) == 0)
		? ((this->candidate_count) / XYB_HW_CB_NUM)
		: (((this->candidate_count) / XYB_HW_CB_NUM) + 1));

	this->genbuttonstate();
	update();
} /* xyb_handwriting::recognize */


void xyb_handwriting::paintEvent (QPaintEvent *evt) {
	QPainter painter(this);

	/*
	painter.drawLine(0, 125, 250, 125);
	painter.drawLine(125, 0, 125, 250);
	*/
	/* - */
	QPen pen_b;
	pen_b.setStyle(Qt::DotLine);
	painter.setPen(pen_b);
	painter.drawLine(0, xyb_handwriting::writor_height_fixed/2,
		xyb_handwriting::writor_width_fixed,
		xyb_handwriting::writor_height_fixed/2);
	/* | */
	painter.drawLine(xyb_handwriting::writor_width_fixed/2, 0,
		xyb_handwriting::writor_width_fixed/2,
		xyb_handwriting::writor_height_fixed);

	QPen pen;
	pen.setWidth(xyb_handwriting::writor_pen_w_fixed);
	painter.setPen(pen);

	// painter.save();
	// painter.restore();

	// 已经录入的points
	int i;
	for (i = 0; i + 4 < this->pnts_c.count() ; i += 4) {
		if ((0x0000 == this->pnts_e[i])
			&& (0x0000 == this->pnts_e[i + 1])
			&& (0x0000 == this->pnts_e[i + 2])
			&& (0x0000 == this->pnts_e[i + 3])
			&& (0x0000 == this->pnts_e[i + 4])) {
			painter.drawLine((float)this->pnts_c[i],
				(float)this->pnts_r[i],
				(float)this->pnts_c[i + 4],
				(float)this->pnts_r[i + 4]);
		}
	} /* for */

	/*
	 * when last NOT hide yet
	 */
	if ((i > 0) && (! this->up->isHidden())) {
		this->up->hide();
		this->down->hide();
		for (i = 0; i < XYB_HW_CB_NUM; ++i) {
			this->candidate_btns[i]->hide();
		}
	}
} /* xyb_handwriting::paintEvent */
/*
 * end of file xyb_handwriting.cpp
 */
